package com.xzz.mybatis.generator.utils;

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.beans.BeanCopier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;

/**
 * Bean属性对拷工具类
 * @Author 朱智贤
 * @Date 2019-07-31
 */
@Slf4j
public final class MyBeanUtil {

    private static Logger logger = LoggerFactory.getLogger(MyBeanUtil.class);

    private MyBeanUtil() {
        throw new IllegalStateException("Utility class");
    }

    /**浅拷贝成hashset*/
    public static final int COPY_TO_HASHSET = 1;
    /**浅拷贝成arrayList*/
    public static final int COPY_TO_ARRAYLIST = 2;

    /**
     * 使用cglib实现高性能浅对象拷贝, 理论上性能是apache和spring beanutil 100倍以上<br/>
     * 缺点:属性和类型必须一致，而且必须要有getter和setter方法, 否则无法拷贝，<br/>
     * 参阅: <br/>
     * https://blog.csdn.net/SJZYLC/article/details/81203086 <br/>
     * https://www.e-learn.cn/content/qita/1733178
     * @param source
     * @param target
     */
    public static void copyShallow(Object source, Object target){
        if(source==null || target==null) return;
        BeanCopier beanCopier = MyBeanCopierGenerator.getBeanCopier(source.getClass(), target.getClass());
        beanCopier.copy(source, target, null);
    }

    /**
     * Collection高性能集合类对象转换，浅拷贝
     * {@link #COPY_TO_HASHSET}  {@link #COPY_TO_ARRAYLIST}
     * @param sourceCollection 源集合对象
     * @param sourceClass 源集合对象类信息
     * @param targetClass 目标对象类信息, 必须要有空构造器
     * @param copyType
     * @return
     */
    public static Collection copyCollectionShallow(Collection sourceCollection, Class sourceClass,
                                                           Class targetClass, int copyType){
        Collection targetCollection = null;
        if(MyCollectionUtil.isEmptyOrNull(sourceCollection)) return targetCollection;
        BeanCopier beanCopier = MyBeanCopierGenerator.getBeanCopier(sourceClass, targetClass);
        switch (copyType){
            case COPY_TO_HASHSET:
                targetCollection = new HashSet(sourceCollection.size());
                break;
            case COPY_TO_ARRAYLIST:
                targetCollection = new ArrayList(sourceCollection.size());
                break;
            default:
                throw new IllegalArgumentException("copyType is not right");
        }
        for(Object source : sourceCollection){
            try {
                Object targetObject = targetClass.newInstance();
                beanCopier.copy(source, targetObject, null);
                targetCollection.add(targetObject);
            }catch (IllegalAccessException|InstantiationException e){
                logger.warn("copyCollectionShallow error", e);
            }
        }
        return targetCollection;
    }

    /**
     * List高性能集合类对象转换，浅拷贝
     * @param sourceCollection 源集合对象
     * @param sourceClass 源集合对象类信息
     * @param targetClass 目标对象类信息, 必须要有空构造器
     * @return
     */
    public static <T> List<T> copyListShallow(Collection sourceCollection, Class sourceClass,
                                                   Class<T> targetClass){
        return (List<T>)copyCollectionShallow(sourceCollection, sourceClass, targetClass, COPY_TO_ARRAYLIST);
    }

    /**
     * Set高性能集合类对象转换，浅拷贝
     * @param sourceCollection 源集合对象
     * @param sourceClass 源集合对象类信息
     * @param targetClass 目标对象类信息, 必须要有空构造器
     * @return
     */
    public static <T> Set<T> copySetShallow(Collection sourceCollection, Class sourceClass,
                                             Class<T> targetClass){
        return (Set<T>)copyCollectionShallow(sourceCollection, sourceClass, targetClass, COPY_TO_HASHSET);
    }

    /** 利用对象流实现对象深拷贝 */
    public static <T> T copyDeep(T source){
        if(source==null) return null;
        try {
            //序列化
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(source);

            //反序列化
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        }catch (Exception e){
            log.warn("copyDeep failed", e);
        }
        return null;
    }
}
